/* 多线程-TCP 服务端 */

/*【Tip】
 * 多线程共享文件描述符表；
 * */

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <pthread.h>

using namespace std;
#define Judge(ret){if(ret < 0) {return 0;}}    //宏函数判断

/* 子线程入口函数
 * */
struct thread_info    //参数类型
{
    thread_info()
    {
        _new_sockfd = -1;
        memset(&_cli_addr, '0', sizeof(_cli_addr));
        memset(&_ser_addr, '0', sizeof(_ser_addr));
    }

    int _new_sockfd;
    struct sockaddr_in _cli_addr;
    struct sockaddr_in _ser_addr;
};

void* tcp_deal_start(void* arg)
{
    /* 防止僵尸线程：
     * 采用子线程线程分离。
     * 若通过主线程join()等待子线程，则会影响下一轮accept
     * */
    pthread_detach(pthread_self());

    /* 循环收发
     * */
    thread_info* ti = (thread_info*)arg;
    int new_sockfd = ti->_new_sockfd;
    struct sockaddr_in cli_addr = ti->_cli_addr;
    struct sockaddr_in ser_addr = ti->_ser_addr;

    while(1)
    {
        char buf[1024] = {0};
        ssize_t recv_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);    //收数据
        if(recv_size < 0)
        {
            perror("recv");
            continue;
        }
        else if(recv_size == 0)    //对端关闭
        {
            //cout << "peer shutdown!" << endl;
            printf("client[%s:%d] shutdown!\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
            close(new_sockfd);
            delete (ti);
            pthread_exit(NULL);
        }
        printf("server <- client[%s:%d]:%s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);

        memset(buf, '\0', sizeof(buf));
        sprintf(buf,"client <- server[%s:%d]:Get!\n", inet_ntoa(ser_addr.sin_addr), ntohs(ser_addr.sin_port));
        send(new_sockfd, buf, strlen(buf), 0);    //发数据
    }
}

int main()
{
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    //Judge(listen_sockfd);
    if(listen_sockfd < 0)
    {
        perror("socket");
        return 0;
    }

    struct sockaddr_in ser_addr;
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(38989);
    ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    //Judge(ret);
    if(ret < 0)
    {
        perror("bind");
        return 0;
    }

    ret = listen(listen_sockfd, 5);
    //Judge(ret);  
    if(ret < 0)
    {
        perror("listen");
        return 0;
    }

    /*
     * 循环accept已连接套接字
     * */
    while(1)
    {
        // 注：循环accept而不是循环创建子线程，因此accept必须在主线程执行
        struct sockaddr_in cli_addr;
        socklen_t cli_addr_len = sizeof(cli_addr);
        int new_sockfd = accept(listen_sockfd, (struct sockaddr*)&cli_addr, &cli_addr_len);
        if(new_sockfd < 0)
        {
            continue;
        }

        /* 多线程处理accept执行到的新套接字
         * */
        thread_info* ti = new thread_info();
        ti->_new_sockfd = new_sockfd;
        memcpy(&ti->_cli_addr, &cli_addr, cli_addr_len);
        memcpy(&ti->_ser_addr, &ser_addr, sizeof(ser_addr));

        pthread_t tid;
        int ret = pthread_create(&tid, NULL, tcp_deal_start, (void*)ti);
        if(ret < 0)
        {
            close(new_sockfd);
            continue;
        }
    }
    return 0;
}
